<!DOCTYPE html>
<html lang="en">

<head>
  <meta charset="UTF-8">
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
  <link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/openlayers/8.2.0/ol.min.css"
    integrity="sha512-bc9nJM5uKHN+wK7rtqMnzlGicwJBWR11SIDFJlYBe5fVOwjHGtXX8KMyYZ4sMgSL0CoUjo4GYgIBucOtqX/RUQ=="
    crossorigin="anonymous" referrerpolicy="no-referrer" />
  <title>图形绘制2</title>

  <style>
    * {
      margin: 0;
      padding: 0;
      box-sizing: border-box;
    }

    html,
    body {
      height: 100%;
      height: 100%;
    }

    #container {
      width: 100%;
      height: 100%;
      overflow: hidden;
      position: absolute;
    }

    #app {
      width: 100vw;
      height: 100vh;
    }

    .app-map {
      height: 90vh;
    }

    .app-btns {
      height: 10vh;
      border-top: 1px solid #c6e2ff;
      padding: 10px;
      display: flex;
      justify-content: space-between;
    }

    .app-btns button {
      background: #fff;
      border: 1px solid #dcdfe6;
      color: #606266;
      border-radius: 4px;
      cursor: pointer;
      font-size: 18px;
      height: 100%;
      width: 15%;
    }

    .app-btns button:hover {
      color: #409eff;
      border-color: #c6e2ff;
      background-color: #ecf5ff;
    }

    .app-btns button.active {
      color: #fff;
      background-color: #409eff;
      border-color: #409eff;
    }

    .input-range {
      font-size: 16px;
      display: flex;
      width: 30%;
    }

    .input-range button {
      width: 50%;
      margin-left: 12px;
    }

    .input-range .sides {
      display: flex;
      flex-direction: column;
      justify-content: center;
      width: 50%;
      text-align: center;
      font-size: 18px;
    }
  </style>
</head>

<body>
  <div id="app">
    <div class="app-map" id="app-map"></div>
    <div class="app-btns">
      <button :class="{active: 1 === currentId}" @click='handleClickDraw(1)'>画六角星</button>
      <button :class="{active: 2 === currentId}" @click='handleClickDraw(2)'>画矩形</button>
      <div class="input-range">
        <div class="sides">
          <input @change='handleRangeChange' v-model='sides' type="range" id="range" min="3" max="20" />
          <div>
            <span>多边形边数：</span>
            <span v-text='sides'></span>
          </div>
        </div>
        <button :class="{active: 3 === currentId}" @click='handleClickDraw(3)'>画正{{sides}}边形</button>
      </div>
      <button :class="{active: id === ''}" @click='handleClickCancel'>退出绘制</button>
    </div>
  </div>

  <script src="https://cdnjs.cloudflare.com/ajax/libs/openlayers/8.2.0/dist/ol.min.js"
    integrity="sha512-+nvfloZUX7awRy1yslYBsicmHKh/qFW5w79+AiGiNcbewg0nBy7AS4G3+aK/Rm+eGPOKlO3tLuVphMxFXeKeOQ=="
    crossorigin="anonymous" referrerpolicy="no-referrer"></script>
  <script src="https://cdnjs.cloudflare.com/ajax/libs/vue/3.4.14/vue.global.prod.min.js"
    integrity="sha512-huEQFMCpBzGkSDSPVAeQFMfvWuQJWs09DslYxQ1xHeaCGQlBiky9KKZuXX7zfb0ytmgvfpTIKKAmlCZT94TAlQ=="
    crossorigin="anonymous" referrerpolicy="no-referrer"></script>

  <script>
    const { createApp } = Vue;
    // feature图片
    const base64Img = '';
    // 画六角星鼠标悬浮样式
    const starHover = '';
    // 画多边形时鼠标悬浮样式
    const squareHover = '';
    // 画时折线鼠标悬浮样式
    const boxHover = '';
    // 基础样式
    const basePointStyle = {
      src: base64Img,
      scale: 0.6,
      anchor: [0.5, 0.5],
      rotateWithView: true,
      rotation: 0,
      opacity: 1
    };
    // 根据基础样式，创建其他样式
    const createStyle = (imageParams, styleParams) => {
      return new ol.style.Style({
        image: new ol.style.Icon({
          ...basePointStyle,
          ...imageParams
        }),
        ...styleParams
      });
    };
    // 填充样式
    const fill = new ol.style.Fill({ color: 'rgba(0, 0, 0, 0.1)' });
    // 线条样式
    const stroke = new ol.style.Stroke({ color: '#07c160', width: 2 });
    // 基础样式style
    const baseStyle = createStyle();
    // 范围内的点位样式
    const inStyle = createStyle({ scale: 1 });
    // 在地图上绘制的点的样式
    const pointStyle = createStyle({ scale: 2 });
    // 圆，多边形范围外的点位的样式
    const outStyle = createStyle({ opacity: 0.6 });
    // 绘制点时鼠标悬浮的状态
    const hoverStyle = {
      'Star': createStyle({
        src: starHover,
        scale: 0.3
      }, {
        stroke
      }),
      'Box': createStyle({
        src: boxHover,
        scale: 0.3
      }, {
        stroke
      }),
      'Square': createStyle({
        src: squareHover,
        scale: 0.3
      }, {
        stroke
      })
    };
    const vm = createApp({
      data() {
        return {
          map: {},
          drawSource: {},
          draw: null,
          pointLayer: null,
          prevFeature: null,
          currentId: -1,
          sides: 3,
          drawTypes: [{
            type: 'Circle',
            hover: hoverStyle.Star,
            id: 1,
            geometryFunction: function (coordinates, geometry) {
              const center = coordinates[0];
              const last = coordinates[coordinates.length - 1];
              const dx = center[0] - last[0];
              const dy = center[1] - last[1];
              const radius = Math.sqrt(dx * dx + dy * dy);
              const rotation = Math.atan2(dy, dx);
              const newCoordinates = [];
              const numPoints = 12;
              for (let i = 0; i < numPoints; ++i) {
                const angle = rotation + (i * 2 * Math.PI) / numPoints;
                const fraction = i % 2 === 0 ? 1 : 0.5;
                const offsetX = radius * fraction * Math.cos(angle);
                const offsetY = radius * fraction * Math.sin(angle);
                newCoordinates.push([center[0] + offsetX, center[1] + offsetY]);
              }
              newCoordinates.push(newCoordinates[0].slice());
              if (!geometry) {
                geometry = new ol.geom.Polygon([newCoordinates]);
                // geometry = new ol.geom.LineString(coordinates);
              } else {
                geometry.setCoordinates([newCoordinates]);
                // geometry.setCoordinates(coordinates);
              }
              return geometry;
            }
          }, {
            type: 'Circle',
            hover: hoverStyle.Box,
            id: 2,
            geometryFunction: ol.interaction.Draw.createBox()
          }, {
            type: 'Circle',
            id: 3,
            hover: hoverStyle.Square,
            geometryFunction: ol.interaction.Draw.createRegularPolygon(3)
          }]
        }
      },
      methods: {
        // 初始化地图
        initMap() {
          // 创建放置用户绘制的feature的图层
          this.drawSource = new ol.source.Vector();
          this.drawVector = new ol.layer.Vector({
            source: this.drawSource,
          });
          // 高德地图瓦片地址
          const vectorLayer = new ol.layer.Tile({
            source: new ol.source.XYZ({
              url: 'http://wprd04.is.autonavi.com/appmaptile?lang=zh_cn&size=1&style=7&x={x}&y={y}&z={z}'
            })
          });
          //  初始化地图
          this.map = new ol.Map({
            target: 'app-map',
            layers: [vectorLayer, this.drawVector],
            view: new ol.View({
              projection: 'EPSG:3857',
              //设定中心点，因为默认坐标系为 3587，所以要将我们常用的经纬度坐标系4326 转换为 3587坐标系
              center: ol.proj.transform([111.8453154, 32.7383500], 'EPSG:4326', 'EPSG:3857'),
              zoom: 5,
            })
          });
          // 绑定地图事件
          this.bindMapEvt();
          // 渲染1000个点位到地图上
          this.renderPoint(1000);
        },
        // 绑定地图事件
        bindMapEvt() {
          // 监听鼠标点击
          this.map.on('click', (evt) => {
            const clickPoint = ol.proj.transform(evt.coordinate, 'EPSG:3857', 'EPSG:4326')
            console.log('当前点击坐标为 : ' + clickPoint[0].toFixed(7) + ',' + clickPoint[1].toFixed(7));
          });
        },
        // 点击绘制各种图形
        handleClickDraw(id) {
          const { map } = this;
          const item = this.drawTypes.find(e => e.id === id);
          const { type, geometryFunction } = item;
          this.currentId = id;
          // 删除上一种绘制
          this.draw && map.removeInteraction(this.draw);
          // 恢复所有点位的透明度为1
          this.setFeatureOpacity();
          // 根据 type 创建交互
          this.draw = new ol.interaction.Draw({
            source: this.drawSource,
            type: type,
            style: item.hover,
            geometryFunction: geometryFunction
          });
          // 将交互添加到地图
          map.addInteraction(this.draw);
          // 监听绘制完成的事件
          this.draw.on('drawend', (event) => {
            // 获取绘制的feature
            const feature = event.feature;
            // 删除上一个feature
            this.prevFeature && this.drawSource.removeFeature(this.prevFeature);
            this.prevFeature = feature;
            // 将图形内、外的点位设置不同的样式
            this.handleDrawEnd(feature);
          });
        },
        // 多边形边数发生变化，重新设置画多边形的 geometryFunction
        handleRangeChange() {
          const square = this.drawTypes.find(e => e.id === 3);
          // 根据边数重新设置 geometryFunction
          square.geometryFunction = ol.interaction.Draw.createRegularPolygon(parseInt(this.sides));
          // 根据id触发点击事件
          this.handleClickDraw(3);
        },
        // 处理绘制圆和多边形的处理
        handleDrawEnd(feature) {
          // 设置绘制完成后圆和多边形的样式
          feature.setStyle(new ol.style.Style({
            fill: new ol.style.Fill({ color: 'rgba(255, 0, 0, 0)' }),
            stroke: new ol.style.Stroke({ color: '#07c160', width: 2 })
          }));
          // 获取feature的Geometry
          const geometry = feature.getGeometry();
          // 遍历1000个点位
          this.pointLayer.getSource().forEachFeature(function (point) {
            const pointGeometry = point.getGeometry();
            // 判断监控点位是否在范围内
            if (geometry.intersectsExtent(pointGeometry.getExtent())) {
              point.setStyle(inStyle); // 在范围内
            } else {
              point.setStyle(outStyle); // 不在范围内
            }
          });
        },
        // 点击取消，删除上一个交互
        handleClickCancel() {
          this.currentId = 'none';
          this.draw && this.map.removeInteraction(this.draw);
          this.draw = null;
        },
        // 恢复点位状态
        setFeatureOpacity() {
          // 遍历1000个点位，恢复样式
          this.pointLayer.getSource().forEachFeature(function (monitorFeature) {
            monitorFeature.setStyle(baseStyle);
          });
        },
        // 在地图上添加点位
        renderPoint(numPoints) {
          const positions = this.createCircularPosition(numPoints); // 生成坐标数据
          // 根据positions创建一个新的数据源和要素数组，
          const vectorSource = new ol.source.Vector({
            features: positions.map(e => {
              // ol.proj.fromLonLat用于将经纬度坐标从 WGS84 坐标系转换为地图投影坐标系
              const feature = new ol.Feature({
                geometry: new ol.geom.Point(ol.proj.fromLonLat(e)),
                custom: {
                  id: Math.ceil(Math.random() * 100000)
                }
              });
              feature.setStyle(baseStyle);
              return feature;
            })
          });
          // 创建带有数据源的矢量图层
          this.pointLayer = new ol.layer.Vector({
            source: vectorSource,
            layerID: 'addpointLayer'
          });
          // 将矢量图层添加到地图上
          this.map.addLayer(this.pointLayer);
        },
        // 创建数据
        createCircularPosition(numPoints) {
          const center = [108.55, 34.32];
          const circularManyPosition = [];
          const minLat = 3.86;
          const maxLat = 53.56;
          const minLon = 73.66;
          const maxLon = 135.05;
          for (var i = 0; i < numPoints; i++) {
            var lat = Math.random() * (maxLat - minLat) + minLat;
            var lon = Math.random() * (maxLon - minLon) + minLon;
            circularManyPosition.push([lon, lat]);
          }
          return circularManyPosition;
        }
      },
      mounted() {
        this.initMap();
      }
    }).mount('#app')
  </script>
  
</body>

</html>